#if !defined(__Cloth_h__)
#define __Cloth_h__

// Include GLM - OpenGL Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
#include<hash_map>

using namespace glm;

class Particle;
class Spring;

// Data structure that holds per-particle info in form of triangles' points, used for:
//   1. Sending cloth's particles to GPU for physics calculation.
//   2. Receiving transform feedback from GPU and updating cloth's particles.
//   3. Sending cloth info to GPU for rendering.

struct vertexData
{
	float id; // unique id for identifying the corresponding particle
	glm::vec3 position;
	glm::vec3 normal;
	glm::vec3 color;
	glm::vec3 acceleration;
	glm::vec3 old_position;
};

struct PostTransFeedVertexData
{
	float id; 
	glm::vec3 position;
	//glm::vec3 normal;
	glm::vec3 acceleration_particle; // Common value for all particles independent of triangle in which it appears. Example: Gravity Force.
	glm::vec3 acceleration_triangle; // Acecleration specific to a given triangle, so same particle in different triangles will have different values. Example: Wind Force.
	glm::vec3 old_position;
};

class Cloth
{
public:

	Cloth(float width, float height, int numParticlesWidth, int numParticlesHeight, int clothHangMode);
	~Cloth();

	// Dumps the particles into the vertexData vector in triangle data form
	void refreshVertexBuffer();

	// Calls timeStep method on each particle for calculating its new position
	void timeStep(float damping);
	void applyAllPrings();

	// Adds gravity and wind forces to each particles, can be used for adding other forces also
	void addForce(const vec3 direction);

	// Adds wind force for each triangle which is proportional to the triangle's area
	void windForce(const vec3 wind, float scale);

	// Detects and resolves the collision of the cloth with a Bounding Sphere.
	// When collision is detected, the collided cloth's particles are offsetted accordingly
	bool checkBoundingSphereCollision(const vec3 center,const float radius );
	std::vector<vertexData>& getVertexBuffer(){return m_vertexBuffer;}

	// Gets a particle by its unique id
	Particle& getParticleByID(float id);

	// Updates each particle from the data transfered back from GPU.
	// Used for GPU mode where timeStep, wind and gravity forces are calculated on GPU.
	// We need to update particle's info on CPU for aplying contraints satisfactions which cannot be done on GPU because 
	// for each particles we need access to all its neighbours of 1st and 2nd degrees.
	void updateParticlesFromTransFeed(PostTransFeedVertexData* data, int len);

	int getNumOfTriangles();
	int getNumOfParticleWidth(){return m_nNumParticlesWidth;}
	int getNumOfParticleHeight(){return m_nNumParticlesHeight;}

	void incrSpringIterations(int num);
	int getNumSpringIterations(){ return m_nNumSpringIterations;}

private:
	Particle* getParticle(int x, int y);
	void makeSpring(Particle *p1, Particle *p2);

	// Calculates 2 vectors which correspond to triangle's lines and then calculates their cross product to get the normal
	vec3 calcTriangleNormal(Particle *p1,Particle *p2,Particle *p3);

	// Calcuales the wind force for a triangle 
	void addWindForcesForTriangle(Particle *p1,Particle *p2,Particle *p3, const vec3 wind);

	// Pushes 3 vertices of the same triangle into triangle data buffer
	void pushTriangleVerticesToVertexBuffer(Particle *p1, Particle *p2, Particle *p3, const vec3 color);

	// Pushes a single vertex into triangle data buffer
	void pushVertexToVertexBuffer(Particle* p1, const vec3 color);

	// Priting particle's data to log file. Used for debugging purposes.
	void printParticle(const vertexData& vd);

	// Resets update from 'Transform Feedback Flag' for each particle.
	// This is because each particle appears more then once in transform feedback array, and gravity force, position and old position 
	// are calculated for each instance of the particle on GPU, but they need to be updated only once on CPU. 
	// Only Wind Force is accumulated because Wind Force is calculated for each triangle and each particle belong to several triangles.
	void resetPerParticleAccelAddedInd();

private:

	int m_nNumParticlesWidth; 
	int m_nNumParticlesHeight;
	int m_nNumSpringIterations;
	float m_nWindSpeed;

	std::vector<Particle> m_particles; 
	std::vector<Spring> m_springs;
	std::vector<vertexData> m_vertexBuffer;
	
	// Used for debugging purposes
	FILE* m_pLog;

	// Hash map which maps particle's id to the pointer of the corresponding particle in order to retrieve the particle in O(1) on average
	std::hash_map<float, Particle*> m_mapPartIdToPartPointer;
};

#endif // __Cloth_h__